package com.stars.easyms.schedule.handler;

import com.stars.easyms.schedule.constant.DistributedScheduleConstants;
import com.stars.easyms.schedule.enums.LoginStatus;
import com.stars.easyms.schedule.util.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 分布式调度框架HandlerMapping处理器
 *
 * @author guoguifang
 */
public class DbScheduleHandlerMapping extends RequestMappingHandlerMapping {

    private final Set<DbScheduleMvcHandler> handlers;
    private String prefix;
    private final Map<String, DbScheduleMvcHandler> MVC_HANDLER_MAP = new ConcurrentHashMap<>(64);
    private final Map<String, HandlerMethod> HANDLER_METHOD_MAP = new ConcurrentHashMap<>(64);

    public DbScheduleHandlerMapping(String prefix, Set<DbScheduleMvcHandler> handlers, ApplicationContext context) {
        this.prefix = prefix;
        this.handlers = handlers;
        this.setOrder(-100);
        this.setUseSuffixPatternMatch(false);
        this.setApplicationContext(context);
        this.init();
    }

    private void init() {
        super.afterPropertiesSet();
        for (DbScheduleMvcHandler handler : handlers) {
            this.detectHandlerMethods(handler);
        }
    }

    @Override
    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
        if (mapping != null && handler instanceof DbScheduleMvcHandler) {
            String[] patterns = this.getPatterns((DbScheduleMvcHandler) handler, mapping);
            if (patterns != null && patterns.length > 0) {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                for (String pattern : patterns) {
                    MVC_HANDLER_MAP.put(pattern.toLowerCase(), (DbScheduleMvcHandler) handler);
                    HANDLER_METHOD_MAP.put(pattern.toLowerCase(), handlerMethod);
                }
                super.registerHandlerMethod(handler, method, this.withNewPatterns(mapping, patterns));
            }
        }
    }

    @Override
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        String formattingPath = StringUtils.formattingPath(lookupPath.toLowerCase());
        HandlerMethod handlerMethod = HANDLER_METHOD_MAP.get(formattingPath);
        if (handlerMethod != null) {
            DbScheduleMvcHandler mvcHandler = MVC_HANDLER_MAP.get(formattingPath);
            LoginStatus loginStatus = mvcHandler.checkAndRefreshLoginStatus(request);
            if (LoginStatus.SUCCESS != loginStatus) {
                request.setAttribute("loginStatus", loginStatus);
                return HANDLER_METHOD_MAP.get(StringUtils.formattingPath(DistributedScheduleConstants.LOGIN_EXCEPTION_HANDLER.toLowerCase()));
            }
            return handlerMethod;
        }
        return super.lookupHandlerMethod(lookupPath, request);
    }

    private String[] getPatterns(DbScheduleMvcHandler handler, RequestMappingInfo mapping) {
        String path = this.getPath(handler);
        return this.getHandlerPatterns(path, mapping);
    }

    protected String getPath(DbScheduleMvcHandler handler) {
        return StringUtils.formattingPath(handler.getPath());
    }

    private String[] getHandlerPatterns(String path, RequestMappingInfo mapping) {
        String patternPrefix = StringUtils.isNotBlank(this.prefix) ? this.prefix + path : path;
        Set<String> defaultPatterns = mapping.getPatternsCondition().getPatterns();
        if (defaultPatterns.isEmpty()) {
            return new String[]{patternPrefix, patternPrefix + ".json"};
        } else {
            List<String> patterns = new ArrayList<>(defaultPatterns);
            for (int i = 0; i < patterns.size(); ++i) {
                patterns.set(i, patternPrefix + StringUtils.formattingPath(patterns.get(i)));
            }
            return patterns.toArray(new String[patterns.size()]);
        }
    }

    private RequestMappingInfo withNewPatterns(RequestMappingInfo mapping, String[] patternStrings) {
        PatternsRequestCondition patterns = new PatternsRequestCondition(patternStrings, null, null, this.useSuffixPatternMatch(), this.useTrailingSlashMatch(), null);
        return new RequestMappingInfo(patterns, mapping.getMethodsCondition(), mapping.getParamsCondition(), mapping.getHeadersCondition(), mapping.getConsumesCondition(), mapping.getProducesCondition(), mapping.getCustomCondition());
    }
}
